# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from __future__ import absolute_import, print_function

import re

from .util import fix_fileid, DIRNAME, NORMCASE
from .info import ParentInfo


FILE_ID_RE = re.compile(r"""
        ^
        (?:
          ( .* [.] (?: py | txt ) \b ) # .txt for doctest files
          ( [^.] .* )?
          )
        $
        """, re.VERBOSE)


def fix_nodeid(nodeid, kind, rootdir=None, #*,
               _fix_fileid=fix_fileid,
               ):
    if not nodeid:
        raise ValueError('missing nodeid')
    if nodeid == '.':
        return nodeid

    fileid = nodeid
    remainder = ''
    if kind not in ('folder', 'file'):
        m = FILE_ID_RE.match(nodeid)
        if m:
            fileid, remainder = m.groups()
        elif len(nodeid) > 1:
            fileid = nodeid[:2]
            remainder = nodeid[2:]
    fileid = _fix_fileid(fileid, rootdir)
    return fileid + (remainder or '')


class DiscoveredTests(object):
    """A container for the discovered tests and their parents."""

    def __init__(self):
        self.reset()

    def __len__(self):
        return len(self._tests)

    def __getitem__(self, index):
        return self._tests[index]

    @property
    def parents(self):
        return sorted(self._parents.values(),
                      key=lambda p: (NORMCASE(p.root or p.name),
                                     p.id),
                      )

    def reset(self):
        """Clear out any previously discovered tests."""
        self._parents = {}
        self._tests = []

    def add_test(self, test, parents):
        """Add the given test and its parents."""
        parentid = self._ensure_parent(test.path, parents)
        # Updating the parent ID and the test ID aren't necessary if the
        # provided test and parents (from the test collector) are
        # properly generated.  However, we play it safe here.
        test = test._replace(
                id=fix_nodeid(test.id, 'test', test.path.root),
                parentid=parentid,
                )
        self._tests.append(test)

    def _ensure_parent(self, path, parents, #*,
                       _dirname=DIRNAME,
                       ):
        rootdir = path.root
        relpath = path.relfile

        _parents = iter(parents)
        nodeid, name, kind = next(_parents)
        # As in add_test(), the node ID *should* already be correct.
        nodeid = fix_nodeid(nodeid, kind, rootdir)
        _parentid = nodeid
        for parentid, parentname, parentkind in _parents:
            # As in add_test(), the parent ID *should* already be correct.
            parentid = fix_nodeid(parentid, kind, rootdir)
            if kind in ('folder', 'file'):
                info = ParentInfo(nodeid, kind, name, rootdir, relpath, parentid)
                relpath = _dirname(relpath)
            else:
                info = ParentInfo(nodeid, kind, name, rootdir, None, parentid)
            self._parents[(rootdir, nodeid)] = info
            nodeid, name, kind = parentid, parentname, parentkind
        assert nodeid == '.'
        info = ParentInfo(nodeid, kind, name=rootdir)
        self._parents[(rootdir, nodeid)] = info

        return _parentid
